/**
 * 
 */
package com.example.demo.service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.demo.util.FlowNodeVo;
import com.example.demo.util.FlowableUtils;

/**
 * @author 030998
 *
 */
@Service
public class FlowTaskService {
	@Autowired
	private RuntimeService runtimeService;
	@Autowired
	private TaskService taskService;
	@Autowired
	private ProcessEngine processEngine;
	@Autowired
	private RepositoryService repositoryService;
	@Autowired
    private HistoryService historyService;
	
	/**
	 * 启动
	 * 
	 * @param processKey
	 * @param businessKey
	 * @param map
	 * @return
	 */
	public String start(String processKey, String businessKey, Map<String, Object> map) {
		ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processKey, businessKey, map);
		return processInstance.getId();
	}

	/**
	 * 停止
	 * 
	 * @param processInstanceId
	 * @param reason
	 */
	public void stop(String processInstanceId, String reason) {
		runtimeService.deleteProcessInstance(processInstanceId, reason);
	}

	/**
	 * 按用户查询
	 * @param userId
	 * @return
	 */
	public List<Task> getListByUserId(String userId) {
		List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).orderByTaskCreateTime().desc().list();
		for (Task task : tasks) {
			System.out.println(task.toString());
		}
		return tasks;
	}

	/**
	 * 按组查询
	 * @param group
	 * @return
	 */
	public List<Task> getListByGroup(String group) {
		List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup(group).orderByTaskCreateTime().desc()
				.list();
		for (Task task : tasks) {
			System.out.println(task.toString());
		}
		return tasks;
	}

	/**
	 * 执行完成
	 * @param taskId
	 * @param map
	 */
	public void complete(String taskId, Map<String, Object> map) {
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		if (task == null) {
			throw new RuntimeException("流程不存在");
		}
		taskService.complete(taskId, map);
		System.out.println("flow complete...........");
	}

	/**
	 * 按businessKey查询任务
	 * @param list
	 * @param businessKey
	 * @return
	 */
	public Task getOneByBusinessKey(@NonNull List<Task> list, String businessKey) {
		Task task = null;
		for (Task t : list) {
			// 通过任务对象获取流程实例
			ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(t.getProcessInstanceId())
					.singleResult();
			if (businessKey.equals(pi.getBusinessKey())) {
				task = t;
			}
		}
		return task;
	}

	/**
	 * 启动流程并跳过第一个节点
	 * @param processKey
	 * @param businessKey
	 * @param map
	 */
	public void startAndComplete(String processKey, String businessKey, Map<String, Object> map) {
		String processInstanceId = start(processKey, businessKey, map);
		System.out.println("processInstanceId..........." + processInstanceId);
		Task task = processEngine.getTaskService().createTaskQuery().processInstanceId(processInstanceId)
				.singleResult();
		taskService.complete(task.getId(), map);
		System.out.println("startAndComplete complete...........");
	}

	/**
	 * 驳回到之前某个点，不适用于并行网关类等复杂的流程
	 * @param currentTaskId
	 * @param targetTaskKey
	 */
	public void backToStep(String currentTaskId, String targetTaskKey) {
		Task currentTask = taskService.createTaskQuery().taskId(currentTaskId).singleResult();
		List<Task> taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId())
				.list();

//		ExecutionEntity execution = (ExecutionEntity) processEngine.getRuntimeService().createExecutionQuery()
//				.executionId(currentTask.getExecutionId()).singleResult();
//		BpmnModel bpmModel = repositoryService.getBpmnModel(currentTask.getProcessDefinitionId());
//		FlowNode flowNode = (FlowNode) bpmModel.getFlowElement(execution.getCurrentActivityId());
//		SequenceFlow sequenceFlow = flowNode.getIncomingFlows().get(0);
//		String sourceRef = sequenceFlow.getSourceRef();

		// Task targetTask =
		// taskService.createTaskQuery().taskId(targetTaskKey).singleResult();
		
		List<String> activeIdList = runtimeService.getActiveActivityIds(currentTask.getProcessInstanceId());
		activeIdList.forEach(d -> System.out.println(d));

		if (taskList == null) {
			throw new RuntimeException("当前任务节点不存在");
		}
		List<String> currentTaskKeys = new ArrayList<>();
		taskList.forEach(t -> currentTaskKeys.add(t.getTaskDefinitionKey()));
		runtimeService.createChangeActivityStateBuilder().processInstanceId(currentTask.getProcessInstanceId())
				.moveActivityIdsToSingleActivityId(currentTaskKeys, targetTaskKey).changeState();
		
		System.out.println("backToStep complete...........");
	}

	/**
	 * 认领任务
	 * @param taskId
	 * @param userId
	 */
	public void claim(String taskId, String userId) {
		processEngine.getTaskService().claim(taskId, userId);
		System.out.println("claim complete...........");
	}
	
	//跳回到首个流程环节点
	@Transactional(readOnly = false)
	public void turnBackFirst(String taskId,Map<String, Object> map) {
		// 当前任务 task
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		if (task.isSuspended()) {
			System.out.println("任务处于挂起状态");
            return ;
        }
        
        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // 获取所有节点信息
        org.flowable.bpmn.model.Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        // 获取全部节点列表，包含子节点
        Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
        
        List<HistoricTaskInstance> historicTaskIdList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list();
        
        
        //List<Task> taskList = new ArrayList<>();
        List<String> currentTaskKeys = new ArrayList<>();
        currentTaskKeys.add(task.getTaskDefinitionKey());
		//taskList.forEach(t -> currentTaskKeys.add(t.getTaskDefinitionKey()));
        runtimeService.createChangeActivityStateBuilder().processInstanceId(task.getProcessInstanceId())
		.moveActivityIdsToSingleActivityId(currentTaskKeys, historicTaskIdList.get(0).getTaskDefinitionKey()).changeState();
	}
	
	/**
	 * 有问题，当流程点有两条线路，一条未实际发生，也会退回，多出一条
	 * 驳回前一个节点
	 * @param taskId
	 */
	@Transactional(readOnly = false)
	public void turnBack(String taskId,Map<String, Object> map) {
		// 当前任务 task
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		if (task.isSuspended()) {
			System.out.println("任务处于挂起状态");
            return ;
        }
        
        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // 获取所有节点信息
        org.flowable.bpmn.model.Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        // 获取全部节点列表，包含子节点
        Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
        // 获取当前任务节点元素
        FlowElement source = null;
        if (allElements != null) {
            for (FlowElement flowElement : allElements) {
                // 类型为用户节点
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    // 获取节点信息
                    source = flowElement;
                }
            }
        }


        // 目的获取所有跳转到的节点 targetIds
        // 获取当前节点的所有父级用户任务节点
        // 深度优先算法思想：延边迭代深入
        List<UserTask> parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null);
        if (parentUserTaskList == null || parentUserTaskList.size() == 0) {
        	System.out.println("当前节点为初始任务节点，不能驳回");
            return ;
        }
        // 获取活动 ID 即节点 Key
        List<String> parentUserTaskKeyList = new ArrayList<>();
        parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId()));
        // 2021.03.03修改：之前使用的是历史任务表获取数据改为使用历史活动表获取数据，因为发现并行情况下，只有一个流程完成的情况下，会出现单独停留在网关的情况，因此扭转时网关也是需要进行扭转的点，网关数据历史活动表才能获取到
        // 后续相关部分同步修改，以及工具类中部分同步修改
        // 获取全部历史节点活动实例，即已经走过的节点历史，数据采用开始时间升序
        List<HistoricTaskInstance> historicTaskIdList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list();
        List<HistoricActivityInstance> historicActivityIdList = historyService.createHistoricActivityInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricActivityInstanceStartTime().asc().list();
        // 数据清洗，将回滚导致的脏数据清洗掉
        

        List<String> lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskIdList);
        // 此时历史任务实例为倒序，获取最后走的节点
        List<String> targetIds = new ArrayList<>();
        // 循环结束标识，遇到当前目标节点的次数
        int number = 0;
        StringBuilder parentHistoricTaskKey = new StringBuilder();
        for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) {
            // 当会签时候会出现特殊的，连续都是同一个节点历史数据的情况，这种时候跳过
            if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) {
                continue;
            }
            parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey);
            if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) {
                number ++;
            }
            // 在数据清洗后，历史节点就是唯一一条从起始到当前节点的历史记录，理论上每个点只会出现一次
            // 在流程中如果出现循环，那么每次循环中间的点也只会出现一次，再出现就是下次循环
            // number == 1，第一次遇到当前节点
            // number == 2，第二次遇到，代表最后一次的循环范围
            if (number == 2) {
                break;
            }
            // 如果当前历史节点，属于父级的节点，说明最后一次经过了这个点，需要退回这个点
            if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) {
                targetIds.add(historicTaskInstanceKey);
            }
        }



        // 目的获取所有需要被跳转的节点 currentIds
        // 取其中一个父级任务，因为后续要么存在公共网关，要么就是串行公共线路
        UserTask oneUserTask = parentUserTaskList.get(0);
        // 获取所有正常进行的执行任务的活动节点ID，这些任务不能直接使用，需要找出其中需要撤回的任务
        List<Execution> runExecutionList = runtimeService.createExecutionQuery().processInstanceId(task.getProcessInstanceId()).list();
        List<String> runActivityIdList = new ArrayList<>();
        runExecutionList.forEach(item -> {
            if (StringUtils.isNotBlank(item.getActivityId())) {
                runActivityIdList.add(item.getActivityId());
            }
        });
        // 需驳回任务列表
        List<String> currentIds = new ArrayList<>();
        // 通过父级网关的出口连线，结合 runExecutionList 比对，获取需要撤回的任务
        List<UserTask> currentFlowElementList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runActivityIdList, null, null);
        currentFlowElementList.forEach(item -> currentIds.add(item.getId()));



        // 规定：并行网关之前节点必须需存在唯一用户任务节点，如果出现多个任务节点，则并行网关节点默认为结束节点，原因为不考虑多对多情况
        if (targetIds.size() > 1 && currentIds.size() > 1) {
        	System.out.println("任务出现多对多情况，无法撤回");
            return ;
        }



        // 2021.03.03修改：添加需撤回的节点为网关时，添加网关的删除信息
        AtomicReference<List<HistoricActivityInstance>> tmp = new AtomicReference<>();
        // 用于下面新增网关删除信息时使用
        String targetTmp = String.join(",", targetIds);
        // currentIds 为活动ID列表
        // currentExecutionIds 为执行任务ID列表
        // 需要通过执行任务ID来设置驳回信息，活动ID不行
        List<String> currentExecutionIds = new ArrayList<>();
        currentIds.forEach(currentId -> runExecutionList.forEach(runExecution -> {
            if (StringUtils.isNotBlank(runExecution.getActivityId()) && currentId.equals(runExecution.getActivityId())) {
                currentExecutionIds.add(runExecution.getId());
                // 查询当前节点的执行任务的历史数据
                tmp.set(historyService.createHistoricActivityInstanceQuery().processInstanceId(task.getProcessInstanceId()).executionId(runExecution.getId()).activityId(runExecution.getActivityId()).list());
                // 如果这个列表的数据只有 1 条数据
                // 网关肯定只有一条，且为包容网关或并行网关
                // 这里的操作目的是为了给网关在扭转前提前加上删除信息，结构与普通节点的删除信息一样，目的是为了知道这个网关也是有经过跳转的
                if (tmp.get() != null && tmp.get().size() == 1 && StringUtils.isNotBlank(tmp.get().get(0).getActivityType())
                        && ("parallelGateway".equals(tmp.get().get(0).getActivityType()) || "inclusiveGateway".equals(tmp.get().get(0).getActivityType()))) {
                    // singleResult 能够执行更新操作
                    // 利用 流程实例ID + 执行任务ID + 活动节点ID 来指定唯一数据，保证数据正确
                    historyService.createNativeHistoricActivityInstanceQuery().sql("UPDATE ACT_HI_ACTINST SET DELETE_REASON_ = 'Change activity to "+ targetTmp +"'  WHERE PROC_INST_ID_='"+ task.getProcessInstanceId() +"' AND EXECUTION_ID_='"+ runExecution.getId() +"' AND ACT_ID_='"+ runExecution.getActivityId() +"'").singleResult();
                }
            }
        }));
        // 设置驳回信息
        AtomicReference<Task> atomicCurrentTask = new AtomicReference<>();
        currentExecutionIds.forEach(item -> {
            atomicCurrentTask.set(taskService.createTaskQuery().executionId(currentExecutionIds.get(0)).singleResult());
            // 类型为网关时，获取用户任务为 null
            if (atomicCurrentTask.get() != null) {
                taskService.addComment(atomicCurrentTask.get().getId(), task.getProcessInstanceId(), "taskStatus", "reject");
                taskService.addComment(atomicCurrentTask.get().getId(), task.getProcessInstanceId(), "taskMessage", "已驳回");
                taskService.addComment(atomicCurrentTask.get().getId(), task.getProcessInstanceId(), "taskComment", "不同意啊不同意，驳回驳回");
            }
        });
        try {
            // 如果父级任务多于 1 个，说明当前节点不是并行节点，原因为不考虑多对多情况
            if (targetIds.size() > 1) {
                // 1 对 多任务跳转，currentIds 当前节点(1)，targetIds 跳转到的节点(多)
                runtimeService.createChangeActivityStateBuilder().processInstanceId(task.getProcessInstanceId()).moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState();
            }
            // 如果父级任务只有一个，因此当前任务可能为网关中的任务
            if (targetIds.size() == 1) {
                // 1 对 1 或 多 对 1 情况，currentIds 当前要跳转的节点列表(1或多)，targetIds.get(0) 跳转到的节点(1)
                runtimeService.createChangeActivityStateBuilder().processInstanceId(task.getProcessInstanceId()).moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState();
            }
        } catch (FlowableObjectNotFoundException e) {
            e.printStackTrace();
        } catch (FlowableException e) {
            e.printStackTrace();
        }
	}
	
	/**
	 * 驳回前指定节点
	 * @param taskId
	 * @param targetKey
	 */
	public void flowReturn(String taskId, String targetKey) {
		// 当前任务 task
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
		if (task.isSuspended()) {
			System.out.println("任务处于挂起状态");
            return ;
        }
        
        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // 获取所有节点信息，暂不考虑子流程情况
        org.flowable.bpmn.model.Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        // 获取全部节点列表，包含子节点
        Collection<FlowElement> allElements = FlowableUtils.getAllElements(process.getFlowElements(), null);
        // 获取当前任务节点元素
        FlowElement source = null;
        // 获取跳转的节点元素
        FlowElement target = null;
        if (allElements != null) {
            for (FlowElement flowElement : allElements) {
                // 当前任务节点元素
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    source = flowElement;
                }
                // 跳转的节点元素
                if (flowElement.getId().equals(targetKey)) {
                    target = flowElement;
                }
            }
        }
        
        // 从当前节点向前扫描
        // 如果存在路线上不存在目标节点，说明目标节点是在网关上或非同一路线上，不可跳转
        // 否则目标节点相对于当前节点，属于串行
        Boolean isSequential = FlowableUtils.iteratorCheckSequentialReferTarget(source, targetKey, null, null, null);
        if (!isSequential) {
        	System.out.println("当前节点相对于目标节点，不属于串行关系，无法回退");
            return ;
        }



        // 获取所有正常进行的执行任务节点的活动ID，这些任务不能直接使用，需要找出其中需要撤回的任务
        List<Execution> runExecutionList = runtimeService.createExecutionQuery().processInstanceId(task.getProcessInstanceId()).list();
        List<String> runActivityIdList = new ArrayList<>();
        runExecutionList.forEach(item -> {
            if (StringUtils.isNotBlank(item.getActivityId())) {
                runActivityIdList.add(item.getActivityId());
            }
        });
        // 需退回任务列表
        List<String> currentIds = new ArrayList<>();
        // 通过父级网关的出口连线，结合 runExecutionList 比对，获取需要撤回的任务
        List<UserTask> currentFlowElementList = FlowableUtils.iteratorFindChildUserTasks(target, runActivityIdList, null, null);
        currentFlowElementList.forEach(item -> currentIds.add(item.getId()));


        // 2021.03.03修改：添加需撤回的节点为网关时，添加网关的删除信息
        AtomicReference<List<HistoricActivityInstance>> tmp = new AtomicReference<>();
        // currentIds 为活动ID列表
        // currentExecutionIds 为执行任务ID列表
        // 需要通过执行任务ID来设置驳回信息，活动ID不行
        List<String> currentExecutionIds = new ArrayList<>();
        currentIds.forEach(currentId -> runExecutionList.forEach(runExecution -> {
            if (StringUtils.isNotBlank(runExecution.getActivityId()) && currentId.equals(runExecution.getActivityId())) {
                currentExecutionIds.add(runExecution.getId());
                // 查询当前节点的执行任务的历史数据
                tmp.set(historyService.createHistoricActivityInstanceQuery().processInstanceId(task.getProcessInstanceId()).executionId(runExecution.getId()).activityId(runExecution.getActivityId()).list());
                // 如果这个列表的数据只有 1 条数据
                // 网关肯定只有一条，且为包容网关或并行网关
                // 这里的操作目的是为了给网关在扭转前提前加上删除信息，结构与普通节点的删除信息一样，目的是为了知道这个网关也是有经过跳转的
                if (tmp.get() != null && tmp.get().size() == 1 && StringUtils.isNotBlank(tmp.get().get(0).getActivityType())
                        && ("parallelGateway".equals(tmp.get().get(0).getActivityType()) || "inclusiveGateway".equals(tmp.get().get(0).getActivityType()))) {
                    // singleResult 能够执行更新操作
                    // 利用 流程实例ID + 执行任务ID + 活动节点ID 来指定唯一数据，保证数据正确
                    historyService.createNativeHistoricActivityInstanceQuery().sql("UPDATE ACT_HI_ACTINST SET DELETE_REASON_ = 'Change activity to "+ targetKey +"'  WHERE PROC_INST_ID_='"+ task.getProcessInstanceId() +"' AND EXECUTION_ID_='"+ runExecution.getId() +"' AND ACT_ID_='"+ runExecution.getActivityId() +"'").singleResult();
                }
            }
        }));
        // 设置驳回信息
        AtomicReference<Task> atomicCurrentTask = new AtomicReference<>();
        currentExecutionIds.forEach(item -> {
            atomicCurrentTask.set(taskService.createTaskQuery().executionId(currentExecutionIds.get(0)).singleResult());
            // 类型为网关时，获取用户任务为 null
            if (atomicCurrentTask.get() != null) {
                taskService.addComment(atomicCurrentTask.get().getId(), task.getProcessInstanceId(), "taskStatus", "return");
                taskService.addComment(atomicCurrentTask.get().getId(), task.getProcessInstanceId(), "taskMessage", "已退回");
                taskService.addComment(atomicCurrentTask.get().getId(), task.getProcessInstanceId(), "taskComment", "流程回退到" + atomicCurrentTask.get().getName() + "节点");
            }
        });

        try {
            // 1 对 1 或 多 对 1 情况，currentIds 当前要跳转的节点列表(1或多)，targetKey 跳转到的节点(1)
            runtimeService.createChangeActivityStateBuilder().processInstanceId(task.getProcessInstanceId()).moveActivityIdsToSingleActivityId(currentIds, targetKey).changeState();
        } catch (FlowableObjectNotFoundException e) {
        	System.out.println("未找到流程实例，流程可能已发生变化");
            e.printStackTrace();
        } catch (FlowableException e) {
        	System.out.println("无法取消或开始活动");
            e.printStackTrace();
        }
        
	}
	
	/**
	 * 查询当前节点可驳回的点，目前可以用，--只测试了简单流程，并行网关复杂流程没测试
	 * @param taskId
	 * @return
	 */
	public List<FlowNodeVo> findReturnUserTask(String taskId) {
		Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        // 获取流程定义信息
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();
        // 获取所有节点信息，暂不考虑子流程情况
        org.flowable.bpmn.model.Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0);
        Collection<FlowElement> flowElements = process.getFlowElements();
        // 获取当前任务节点元素
        UserTask source = null;
        if (flowElements != null) {
            for (FlowElement flowElement : flowElements) {
                // 类型为用户节点
                if (flowElement.getId().equals(task.getTaskDefinitionKey())) {
                    source = (UserTask) flowElement;
                    break;
                }
            }
        }
        // 获取节点的所有路线
        List<List<UserTask>> roads = FlowableUtils.findRoad(source, null, null, null);
        // 可回退的节点列表
        List<UserTask> userTaskList = new ArrayList<>();
        for (List<UserTask> road : roads) {
            if (userTaskList.size() == 0) {
                // 还没有可回退节点直接添加
                userTaskList = road;
            } else {
                // 如果已有回退节点，则比对取交集部分
                userTaskList.retainAll(road);
            }
        }
        
        
        
        String sql = "select t.* from act_ru_actinst t where t.ACT_TYPE_ = 'userTask' " +
                " and t.PROC_INST_ID_=#{processInstanceId} and t.END_TIME_ is not null order by START_TIME_ ";
        List<ActivityInstance> activityInstances = runtimeService.createNativeActivityInstanceQuery().sql(sql)
                .parameter("processInstanceId", task.getProcessInstanceId())
                .list();
        
        Map<String, ActivityInstance> activityTemp = new HashMap<String, ActivityInstance>();
        activityInstances.forEach(a -> activityTemp.put(a.getActivityId(), a));
        
        List<FlowNodeVo> backNods = new ArrayList<>();
        userTaskList.forEach(userTask -> {
            FlowNodeVo node = new FlowNodeVo(userTask.getId(), userTask.getName(), null, activityTemp.get(userTask.getId()).getAssignee(), activityTemp.get(userTask.getId()).getEndTime());
            backNods.add(node);
        });
        
        backNods.sort(Comparator.comparing(FlowNodeVo::getEndTime));
        
        return backNods;
	}
	
	/**
	 * 查询目前可以驳回的节点，只返回，会显示驳回之后后续环节的节点，
	 * @param taskId
	 * @param processInstanceId
	 * @return
	 */
	public List<FlowNodeVo> getBackNodesByProcessInstanceId(String taskId, String processInstanceId) {
		List<FlowNodeVo> backNods = new ArrayList<>();
        TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(taskId).singleResult();
        String currActId = taskEntity.getTaskDefinitionKey();
        //获取运行节点表中usertask
        String sql = "select t.* from act_ru_actinst t where t.ACT_TYPE_ = 'userTask' " +
                " and t.PROC_INST_ID_=#{processInstanceId} and t.END_TIME_ is not null ";
        List<ActivityInstance> activityInstances = runtimeService.createNativeActivityInstanceQuery().sql(sql)
                .parameter("processInstanceId", processInstanceId)
                .list();
        //获取运行节点表的parallelGateway节点并出重
        sql = "SELECT t.ID_, t.REV_,t.PROC_DEF_ID_,t.PROC_INST_ID_,t.EXECUTION_ID_,t.ACT_ID_, t.TASK_ID_, t.CALL_PROC_INST_ID_, t.ACT_NAME_, t.ACT_TYPE_, " +
                " t.ASSIGNEE_, t.START_TIME_, max(t.END_TIME_) as END_TIME_, t.DURATION_, t.DELETE_REASON_, t.TENANT_ID_" +
                " FROM  act_ru_actinst t WHERE t.ACT_TYPE_ = 'parallelGateway' AND t.PROC_INST_ID_ = #{processInstanceId} and t.END_TIME_ is not null" +
                " and t.ACT_ID_ <> #{actId} GROUP BY t.act_id_";
        List<ActivityInstance> parallelGatewaies = runtimeService.createActivityInstanceQuery().activityType("parallelGateway").processInstanceId(processInstanceId).orderByActivityInstanceEndTime().asc()
                //.parameter("processInstanceId", processInstanceId)
                //.parameter("actId", currActId)
                .list();
        //排序
        if (parallelGatewaies!=null && parallelGatewaies.size()>0) {
        	for(ActivityInstance activity : parallelGatewaies) {
        		if(StringUtils.equals(currActId, activity.getActivityId())) {
        			continue;
        		}
        		 activityInstances.add(activity);
        	}
            //activityInstances.addAll(parallelGatewaies);
            activityInstances.sort(Comparator.comparing(ActivityInstance::getEndTime));
        }
        //分组节点
        int count = 0;
        Map<ActivityInstance, List<ActivityInstance>> parallelGatewayUserTasks = new HashMap<>();
        List<ActivityInstance> userTasks = new ArrayList<>();
        ActivityInstance currActivityInstance = null;
        for (ActivityInstance activityInstance : activityInstances) {
            if (BpmnXMLConstants.ELEMENT_GATEWAY_PARALLEL.equals(activityInstance.getActivityType())) {
                count++;
                if (count % 2 != 0) {
                    List<ActivityInstance> datas = new ArrayList<>();
                    currActivityInstance = activityInstance;
                    parallelGatewayUserTasks.put(currActivityInstance, datas);
                }
            }
            if (BpmnXMLConstants.ELEMENT_TASK_USER.equals(activityInstance.getActivityType())) {
                if (count % 2 == 0) {
                    userTasks.add(activityInstance);
                } else {
                    if (parallelGatewayUserTasks.containsKey(currActivityInstance)) {
                        parallelGatewayUserTasks.get(currActivityInstance).add(activityInstance);
                    }
                }
            }
        }
        //组装人员名称
        List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId).finished().list();
        Map<String, List<HistoricTaskInstance>> taskInstanceMap = new HashMap<>();
        List<String> userCodes = new ArrayList<>();
        historicTaskInstances.forEach(historicTaskInstance -> {
            userCodes.add(historicTaskInstance.getAssignee());
            String taskDefinitionKey = historicTaskInstance.getTaskDefinitionKey();
            if (taskInstanceMap.containsKey(historicTaskInstance.getTaskDefinitionKey())) {
                taskInstanceMap.get(taskDefinitionKey).add(historicTaskInstance);
            } else {
                List<HistoricTaskInstance> tasks = new ArrayList<>();
                tasks.add(historicTaskInstance);
                taskInstanceMap.put(taskDefinitionKey, tasks);
            }
        });
//        //组装usertask的数据
//        List<User> userList = identityService.createUserQuery().userIds(userCodes).list();
//        Map<String, String> activityIdUserNames = this.getApplyers(processInstanceId, userList, taskInstanceMap);
//        if (CollectionUtils.isNotEmpty(userTasks)) {
//            userTasks.forEach(activityInstance -> {
//                FlowNodeVo node = new FlowNodeVo();
//                node.setNodeId(activityInstance.getActivityId());
//                node.setNodeName(activityInstance.getActivityName());
//                node.setEndTime(activityInstance.getEndTime());
//                node.setUserName(activityIdUserNames.get(activityInstance.getActivityId()));
//                backNods.add(node);
//            });
//        }
        
        userTasks.forEach(activityInstance -> {
            FlowNodeVo node = new FlowNodeVo();
            node.setNodeId(activityInstance.getActivityId());
            node.setNodeName(activityInstance.getActivityName());
            node.setEndTime(activityInstance.getEndTime());
            node.setUserName(activityInstance.getAssignee());
            backNods.add(node);
        });
        
        //组装会签节点数据
        if (taskInstanceMap!=null && taskInstanceMap.isEmpty()) {
            parallelGatewayUserTasks.forEach((activity, activities) -> {
                FlowNodeVo node = new FlowNodeVo();
                node.setNodeId(activity.getActivityId());
                node.setEndTime(activity.getEndTime());
                StringBuffer nodeNames = new StringBuffer("会签:");
                StringBuffer userNames = new StringBuffer("审批人员:");
                if (activities!=null && activities.size()>0){
                    activities.forEach(activityInstance -> {
                        nodeNames.append(activityInstance.getActivityName()).append(",");
                        userNames.append(activityInstance.getAssignee()).append(",");
                    });
                    node.setNodeName(nodeNames.toString());
                    node.setUserName(userNames.toString());
                    backNods.add(node);
                }
            });
        }
        //去重合并
        List<FlowNodeVo> datas = backNods.stream().collect(
                Collectors.collectingAndThen(Collectors.toCollection(() ->
                        new TreeSet<>(Comparator.comparing(nodeVo -> nodeVo.getNodeId()))), ArrayList::new));
 
        //排序
        datas.sort(Comparator.comparing(FlowNodeVo::getEndTime));
        return datas;
	}
	
	
	/**
	 * 执行委派
	 * @param taskId
	 * @param userId
	 * @param delegateUserId
	 */
	@Transactional(readOnly = false)
	public void delegateTask(String taskId,String userId, String delegateUserId) {
		TaskEntityImpl currTask = (TaskEntityImpl) taskService.createTaskQuery().taskId(taskId).singleResult();
		if(currTask!=null) {
			taskService.addComment(taskId, currTask.getProcessInstanceId(), "委托xxx办理");
			taskService.setAssignee(taskId, userId);
			taskService.delegateTask(taskId, delegateUserId);
		}
	}
	
	/**
	 * 处理委派
	 * @param taskId
	 * @param variables
	 */
	@Transactional(readOnly = false)
	public void resolveDelegateTask(String taskId, Map<String, Object> variables) {
		TaskEntityImpl currTask = (TaskEntityImpl) taskService.createTaskQuery().taskId(taskId).singleResult();
		if(DelegationState.PENDING.equals(currTask.getDelegationState())) {
			TaskEntity subTask = this.createSubTask(currTask, "liuziquan");
			taskService.complete(subTask.getId());
			taskService.resolveTask(taskId, variables);
		}
	}
	
	
	/**
	 * 创建子Task
	 * @param ptask
	 * @param assignee
	 * @return
	 */
	protected TaskEntity createSubTask(TaskEntity ptask, String assignee) {
        return this.createSubTask(ptask, ptask.getId(), assignee);
    }
	/**
	 * 创建子Task
	 * @param ptask
	 * @param ptaskId
	 * @param assignee 子Task执行人
	 * @return
	 */
	protected TaskEntity createSubTask(TaskEntity ptask, String ptaskId, String assignee) {
        TaskEntity task = null;
        if (ptask != null) {
            //1.生成子任务
            task = (TaskEntity) taskService.newTask(UUID.randomUUID().toString());
            task.setCategory(ptask.getCategory());
            task.setDescription(ptask.getDescription());
            task.setTenantId(ptask.getTenantId());
            task.setAssignee(assignee);
            task.setName(ptask.getName());
            task.setParentTaskId(ptaskId);
            task.setProcessDefinitionId(ptask.getProcessDefinitionId());
            task.setProcessInstanceId(ptask.getProcessInstanceId());
            task.setTaskDefinitionKey(ptask.getTaskDefinitionKey());
            task.setTaskDefinitionId(ptask.getTaskDefinitionId());
            task.setPriority(ptask.getPriority());
            task.setCreateTime(new Date());
            taskService.saveTask(task);
        }
        return task;
    }
	
	
	/**
	 * 转办
	 * @param taskId
	 * @param userId 处理人员
	 * @param turnToUserId 转办人员
	 */
	@Transactional(readOnly = false)
	public void turnTask(String taskId, String userId, String turnToUserId) {
		TaskEntityImpl currTask = (TaskEntityImpl) taskService.createTaskQuery().taskId(taskId).singleResult();
		if(currTask != null) {
			TaskEntity task = this.createSubTask(currTask, userId);
			taskService.addComment(taskId, currTask.getProcessInstanceId(), "转xxx办理");
			taskService.saveTask(task);
			taskService.complete(task.getId());
			//转办
			taskService.setAssignee(taskId, turnToUserId);
			taskService.setOwner(taskId, userId);
		}
	}

}
